package prac10;
import java.io.*;


/**
 * Part of a prac to give students experience using hash tables. 
 * Modified from prac 4
 * 
 * @author          Dhananjay Thiruvady
 * @modified        Menghong Li, Qian Zhao
 * @since           10/5/2011
 */

public class Dictionary {
	
	
//	public static LinearProbe<Word, Frequency> myHashTable = new LinearProbe<Word, Frequency>(Word.getTableSize());
	public static SeparateChaining<Word, Frequency> myHashTable = new SeparateChaining<Word, Frequency>(Word.getTableSize());
//	public static DynamicLinearProbe<Word, Frequency> myHashTable = new DynamicLinearProbe<Word, Frequency>();
	
    /**
     * @param  none
     * @pre    none
     * @post   see postconditions for individual methods in this class.
     * @complexity  O(U)*OCommand, where U is the number of times the user 
     *     chooses to enter a command (a large number) and OCommand is the 
     *     complexity of the best/worst command
     */
     public static void menu(){
	 boolean quit = false;
	 String inputLine = null;
	 String[] command;
	 BufferedReader console = new BufferedReader(new InputStreamReader(System.in));
	 String menu = "Prac10 Dictionary Menu Options\n" + 
                     "  1. Read file\n" +
                     "  2. Search word\n" +
                     "  3. List words\n" +
                     "  4. Quit\n";
     
         // Command parsing loop
	 while (!quit) {

	     try {
		 System.out.println(menu);       // Print the menu
		 inputLine = console.readLine(); // Read a command
		 command = inputLine.split(" ");
		 
		 if (command[0].equals("1")) { //Read file
			 String filename = null;
             System.out.println("Pleas enter file name: ");
             filename = console.readLine();
             readFromFile(filename);
		 } else if (command[0].equals("2")) { //Search word
			 String str = null;
			 System.out.println("Search word: ");
			 str = console.readLine();
			 Frequency fre = myHashTable.search(new Word(str));
			 if (fre != null) {
		            System.out.println("Word Found!\n Frequency: " +fre);
		     } else {
		            System.out.println("Not found.");
		     }
			 System.out.println("Collisions: " + myHashTable.getCollisions() + " Probes: " + myHashTable.getProbes());
			 
		 } else if (command[0].equals("3")) { //List words
			 myHashTable.printItems();
			
		 } else if (command[0].equals("4")) { //Quit
		     quit = true;
		 } else {
		     System.out.println("Unrecognized command.");
		 }       
	     } catch (IOException e) {
		 System.out.println("Error reading from console");
		 inputLine = "";
	     }
	 }
     }
     
     /**
      * Reads a text file line by line into.
      * 
      * @param       fileName path to file
      * @post        myHashTable contains the entry in the text file
      * @complexity  best and worse case: O(N*(M+I) 
      * 			 where N is the number of lines in the list and
      *              	   M is the complexity of reading a line from file
      *              	   I is the complexity of {@link LinearProbe#insert(Hashable, Object)}};
      */
     public static boolean readFromFile(String fileName) {
         BufferedReader inputReader;
         try {
             // have a go at opening the file
             inputReader = new BufferedReader(new FileReader(fileName));
         } catch (FileNotFoundException e) {
             System.out.println("File not found: " + fileName);
             return false;
         }

         // the file is now open, lets read from it.
         String line;
         try {
             while ((line = inputReader.readLine()) != null) {
            	 String[] element = line.split(" ");
            	 
            	 if (element.length != 2) {
                     return false;
                 }

                 if (!isWord(element[0]) || !isAllDigits(element[1])) {
                     return false;
                 }
            	 
            	 String key = element[0].toLowerCase();
            	 int frequency = Integer.parseInt(element[1]);
            	 if (frequency < 0 || frequency > 3) {
                     return false;
                 }
            	
            	 myHashTable.insert(new Word(key), new Frequency(frequency));
                 
             }
         } catch (IOException e) {
             // if we got here, then the readLine() died
             System.out.println("Error reading from file: " + fileName);
             return false;
         }
         System.out.println("Collisions: " + myHashTable.getCollisions() + " Probes: " + myHashTable.getProbes());
         return true;
     }
     
     /**
      * Recursively works out whether a string is entirely digits
      * 
      * @param 		str the string to be tested 
      * @post		return true if and only if the entire string is non-negative digits
      * @return		true if str is all digits
      * @complexity	Best case:	O(1) when the first character is not a digit
      * 			Worst case: O(N*(P+S)) 
      * 			where N is the size of the string 
      * 				  P is complexity of {@link Integer#parseInt(String)}
      * 				  S is complexity of {@link String#substring(int)}}.
      */
     public static boolean isAllDigits(String str) {
         try {
             if (str.length() == 1) {
                 Integer.parseInt(str.substring(0, 1));
                 return true;
             } else {
                 Integer.parseInt(str.substring(0, 1));
                 return isAllDigits(str.substring(1));
             }
         } catch (NumberFormatException e) {
             return false;
         }
     }

     /**
      * Checks that a string contains only alphabets. Case does not matter.
      * 
      * @return true if the string contains only alphabets 
      *     (specifically, if it belongs to the category "Lu", "Ll", "Lt", "Lm", 
      *     or "Lo" in the Unicode specification data file) otherwise false,
      *     the original string is not altered
      * @complexity  best and worse case: O(N) where N is the number of 
      *     characters in the string (assuming length(), charAt, and isLetter 
      *     are all O(1))
      */
     public static boolean isWord(String s) {
         for (int i = 0; i < s.length(); i++) {
             if (!Character.isLetter(s.charAt(i)) && !(s.charAt(i) == '.')) {
                 return false;
             }
         }
         return true;
     }
     
        /**
     
     * @param  args command-line arguments (not used)
     * @pre    none
     * @post   see postconditions for individual methods in this class.
     * @complexity O(menu())
     */
    /*
     * Please note that the complexities may change as you add 
     * functionality to this class -- don't forget to update this comment 
     * as you go.  
     */
    public static void main(String[] args) {
        menu();
    }

}
